/*******************************************************************************
 * Copyright (c) 2014, 2020 Thales Global Services S.A.S.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 * 
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *    Thales Global Services S.A.S - initial API and implementation
 ******************************************************************************/

package org.polarsys.kitalpha.ad.viewpoint.dsl.generation.explorer.contextual.util;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.codegen.ecore.genmodel.GenFeature;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;

import org.polarsys.kitalpha.ad.viewpoint.dsl.generation.provider.exception.ViewpointResourceException;
import org.polarsys.kitalpha.ad.viewpoint.dsl.generation.provider.resourceimpl.ViewpointResourceProviderRegistry;
import org.polarsys.kitalpha.ad.viewpoint.dsl.as.model.vpdesc.AbstractAssociation;
import org.polarsys.kitalpha.ad.viewpoint.dsl.as.model.vpdesc.AbstractFeature;
import org.polarsys.kitalpha.ad.viewpoint.dsl.as.model.vpdesc.AbstractSuperClass;
import org.polarsys.kitalpha.ad.viewpoint.dsl.as.model.vpdesc.Class;
import org.polarsys.kitalpha.ad.viewpoint.dsl.as.model.vpdesc.LocalSuperClass;

/**
 * @author Boubekeur Zendagui
 */
public class ClassUtils {
	
	public static ClassUtils INSTANCE = new ClassUtils();
	
	private String projectName_;
	private String editPluginName_;
	private EPackage generatedEPackage_ ;
	private GenPackage genPackage_;
	
	
	/**
	 * Initialize from resources providers
	 * @throws ViewpointResourceException
	 */
	public void load() throws ViewpointResourceException{
		generatedEPackage_ = ViewpointResourceProviderRegistry.getInstance().getEcoreProvider().getEPackage();
		editPluginName_ = ViewpointResourceProviderRegistry.getInstance().getGenmodelProvider().getEditPluginName();
		genPackage_ =  ViewpointResourceProviderRegistry.getInstance().getGenmodelProvider().getGenPackage();
	}
	
	/**
	 * Initialize from provided data
	 * @param generatedEPackage the EPackage generated by viewpoint dsl generator 
	 * @param generatedGenModel The genmodel generated by viewpoint dsl generator
	 */
	public void load(EPackage generatedEPackage, GenModel generatedGenModel){
		generatedEPackage_ = generatedEPackage;
		editPluginName_ = generatedGenModel.getEditPluginID();
		genPackage_ = generatedGenModel.getGenPackages().get(0);
	}
	
	/**
	 * 
	 * @param clazz
	 * @return
	 */
	public String getClassInterfaceName(Class clazz){
		// Get the generated EClass from the VPDesc clazz Class
		EClass eClass = getEquivalent(clazz);
		// Get the equivalent genClass from genModel
		GenClass genClass = getEquivalent(eClass);
		return genClass.getInterfaceName();
	}
	
	/**
	 * 
	 * @param clazz
	 * @return
	 */
	public String getClassInterfaceFullyQualifiedName(Class clazz){
		// Get the generated EClass from the VPDesc clazz Class
		EClass eClass = getEquivalent(clazz);
		// Get the equivalent genClass from genModel
		GenClass genClass = getEquivalent(eClass);
		
		return genClass.getQualifiedInterfaceName();
	}
	
	/**
	 * This get the the accessor method name of the EReference generated 
	 * from @param association. This method parameter is an abstract Association.
	 * It handle local and external Associations
	 * @param association
	 * @return name of accessor
	 * @throws Exception 
	 */
	public String getAssociationAccesssorName(AbstractFeature feature) throws Exception{
		if (feature == null){
			throw new Exception();
		}
		String featureName = feature.getName();
		Class clazz = (Class)feature.eContainer();
		// Get the generated EClass from the VPDesc clazz Class
		EClass eClass = getEquivalent(clazz);
		// Get the generated GenFeature 
		GenFeature genFeature = getEquivalent(eClass.getEStructuralFeature(featureName));
		return (genFeature != null ? "get"+genFeature.getAccessorName() : null);
	}
	
	/**
	 * 
	 * @param eClass
	 * @return
	 */
	public GenClass getEquivalent(EClass eClass){
		String eClassPackageNsURI = eClass.getEPackage().getNsURI();
		String className = eClass.getName();
		for (GenClass genClass : genPackage_.getGenClasses()) 
		{
			EClass clazz = genClass.getEcoreClass();
			if (clazz.getName().equals(className) && clazz.getEPackage().getNsURI().equals(eClassPackageNsURI))
				return genClass;
		}
		return null;
	}
	
	/**
	 * 
	 * @param clazz
	 * @return
	 */
	private EClass getEquivalent(Class clazz){
		String className = clazz.getName();
		return (EClass)generatedEPackage_.getEClassifier(className);
	}
	
	/**
	 * 
	 * @param eStructuralFeature
	 * @return
	 */
	private GenFeature getEquivalent(EStructuralFeature eStructuralFeature){
		GenClass genClass = getEquivalent(((EClass)eStructuralFeature.eContainer()));
		
		String eSFName = eStructuralFeature.getName();
		
		for (GenFeature genFeature : genClass.getGenFeatures()) {
			EStructuralFeature structuralFeature = genFeature.getEcoreFeature();
			if (structuralFeature.getName().equals(eSFName))
				return genFeature;
		}
		return null;
	}
	
	/**
	 * This method check if the Association refers/contains many element.
	 * @param abstractAssociation 
	 * @return True if the association refers/contains more then one element, 
	 * False else
	 */
	public static boolean isAssociationMultiple(AbstractAssociation abstractAssociation){
		switch (abstractAssociation.getCardinality()) {
			case NOTHING_OR_MANY:
			case ONE_OR_MANY:
				return true;

			case NOTHING_OR_ONE:
			case ONLY_ONE:
				return false;
		}
		return false;
	}
	
	/**
	 * This method collect all associations of the corresponding Class. 
	 * All Association means the owned one and those one defined in the super 
	 * classes.
	 * @param clazz 
	 * @return List of owned and inherited Associations that refers/contains 
	 * local classes
	 */
	public static List<AbstractAssociation> getAllLocalAssociation(Class clazz){
		List<AbstractAssociation> result = new ArrayList<AbstractAssociation>();
		
		final List<AbstractAssociation> ownedAssociation = clazz.getVP_Classes_Associations(); 
		if (ownedAssociation != null && ownedAssociation.size() > 0)
			result.addAll(ownedAssociation);
		
		final List<AbstractAssociation> inheritedAssociation = getInheritedLocalAssociation(clazz);
		if (inheritedAssociation != null && inheritedAssociation.size() > 0)
			result.addAll(inheritedAssociation);
		
		return result;
	}
	
	/**
	 *  
	 * @param clazz 
	 * @return List of inherited Associations that refers/contains local classes
	 */
	public static List<AbstractAssociation> getInheritedLocalAssociation(Class clazz){
		List<AbstractAssociation> result = new ArrayList<AbstractAssociation>();
		
		if (clazz.getInheritences() != null && clazz.getInheritences().size() > 0)
		{
			for (AbstractSuperClass cls : clazz.getInheritences()) 
			{
				if (cls instanceof LocalSuperClass)
				{
					LocalSuperClass localSuperClass = (LocalSuperClass) cls;
					Class superClass = localSuperClass.getSuperClass();
					final List<AbstractAssociation> inheritedAssociation = superClass.getVP_Classes_Associations();
					if (inheritedAssociation != null && inheritedAssociation.size() > 0)
					{
						result.addAll(inheritedAssociation);
					}
					
					List<AbstractAssociation> inheritedSuperAssociation = getInheritedLocalAssociation(superClass);
					
					if (inheritedSuperAssociation != null && inheritedSuperAssociation.size() > 0)
						result.addAll(inheritedSuperAssociation);
					
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Getter of the projectName_ property 
	 * @return projectName_ value
	 */
	public String getProjectName() {
		return projectName_;
	}

	/**
	 * Setter of the projectName_ property
	 * @param projectName 
	 */
	public void setProjectName(String projectName) {
		this.projectName_ = projectName;
	}
	
	/**
	 *  Getter of the editPluginName_ property
	 * @return editPluginName_ value
	 */
	public String getEditPluginName() {
		return editPluginName_;
	}

	/**
	 * Setter of the editPluginName_ property
	 * @param editPluginName 
	 */
	public void setEditPluginName(String editPluginName) {
		this.editPluginName_ = editPluginName;
	}
}
